package com.qkun.library.base;

import android.app.IntentService;
import android.app.Service;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.IBinder;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.JobIntentService;

import com.qkun.library.utils.Utils;

/**
 * <p>
 * 方法{@link Context#bindService(Intent, ServiceConnection, int)}用于绑定服务，
 * 当返回值为true的时候表示可以绑定该服务，且为true的时候可以解绑它。
 * 绑定的时候传入的flags：
 * 1、0：如果不想设置任何值，可以设置为0
 * 2、{@link Context#BIND_AUTO_CREATE}：
 * 只要绑定存在，就自动创建服务。请注意，尽管这将创建服务，
 * 但由于对{@link #startService}的显式调用，其{@link Service＃onStartCommand}方法仍将仅被调用。
 * 即使没有这些，仍然可以在创建服务时为您提供对服务对象的访问。请注意，在{@link Build.VERSION_CODES＃ICE_CREAM_SANDWICH}之前，
 * 不提供此标志还将影响系统认为目标服务流程的重要性。设置后，唯一的提高方法是从服务绑定，在这种情况下，仅当该活动处于前台时才重要。
 * 现在要实现此行为，您必须显式提供新标志{@link #BIND_ADJUST_WITH_ACTIVITY}。为了兼容，未指定{@link #BIND_AUTO_CREATE}的旧应用程序将
 * 自动为其设置标志{@link #BIND_WAIVE_PRIORITY}和{@link #BIND_ADJUST_WITH_ACTIVITY}，以实现相同的结果。
 * 绑定服务时候，如果服务尚未创建，服务会自动创建，在API LEVEL 14以前的版本不支持这个标志，使用{@link Context#BIND_WAIVE_PRIORITY}可以达到同样效果。
 * 3、{@link Context#BIND_DEBUG_UNBIND}：
 * 包含调试帮助，用于不匹配的取消绑定调用。设置此标志后，将保留以下{@link #unbindService}调用的调用堆栈，
 * 如果以后进行了不正确的取消绑定调用，则将其打印。请注意，执行此操作需要保留有关在应用程序生命周期内进行的绑定的信息，从而导致泄漏-仅应将其用于调试。
 * 通常用于Debug，在<code>unbindService</code>的时候，会将服务信息保存并打印出来，这个标记很容易造成内存泄漏。
 * 4、{@link Context#BIND_NOT_FOREGROUND}：
 * 不允许此绑定将目标服务的过程提高到前台调度优先级。它仍将至少提高到与客户端相同的内存优先级（这样，在客户端不可终止的任何情况下其进程都不会终止），
 * 但是出于CPU调度的目的，它可能留在后台。这仅在绑定客户端为前台进程而目标服务为后台进程的情况下才有影响。
 * 不会将被绑定的服务提升到前台优先级，但是这个服务也至少会和客户端在内存中优先级是相同的。
 * 5、{@link Context#BIND_ABOVE_CLIENT}：
 * 表示绑定到此服务的客户端应用程序认为该服务比应用程序本身更重要。设置后，平台将尝试让内存不足杀手杀死应用程序，
 * 然后再杀死它所绑定的服务，尽管不能保证确实如此。
 * 设置服务的进程优先级高于客户端的优先级，只有当需要服务晚于客户端被销毁这种情况才这样设置。
 * 6、{@link Context#BIND_ALLOW_OOM_MANAGEMENT}：
 * 允许托管绑定服务的进程通过其常规内存管理。它将被视为正在运行的服务，如果它运行了很长时间，
 * 则允许系统在内存不足或其他突发性事件时（临时）删除该进程，并更加积极地使其成为待杀（并重新启动）的候选人。
 * 保持服务受默认的服务管理器管理，当内存不足时候，会销毁服务。
 * 7、{@link Context#BIND_WAIVE_PRIORITY}：
 * 不会影响目标服务的托管进程的调度或内存管理优先级。允许在后台LRU列表上管理该服务的进程，就像在后台执行常规应用程序一样。
 * 不会影响服务的进程优先级，像通用的应用进程一样将服务放在一个LRU表中。
 * 8、{@link Context#BIND_IMPORTANT}：
 * 标识服务对客户端是非常重要的，会将服务提升至前台进程优先级，通常情况下，即使客户端是前台优先级，服务最多也只能被提升至可见进程优先级。
 * 9、{@link Context#BIND_ADJUST_WITH_ACTIVITY}:
 * 允许绑定到即时应用程序提供的服务。请注意，呼叫者可能无权访问提供该服务的即时应用程序，这违反了即时应用程序沙箱。
 * 该标志仅用于开发/测试，应格外小心。仅允许系统使用此标志。
 * 如果客户端是Activity，服务优先级的提高取决于Activity的进程优先级，使用这个标识后，会无视其他标识。
 * </p>
 * <p>
 * 1、启动服务：如果一个Service被Android组件调用startService()方法启动，那么不管这个Service对象是否使用bindService()方法被访问者绑定过，‘
 * 该Service都会在后台运行。因为Android系统只会为一个Service服务创建一个实例，所以无论启动几次，onCreate()方法仅执行一次，
 * 但是每次启动都会执行onStartCommand()方法，一旦服务启动，不管访问者是否被销毁，服务将一直执行下去，
 * 除非被调用stopService()方法或者服务自身的stopSelf()方法，当然系统资源不足的时候，也有可能回收结束服务回收资源。
 * 2、绑定服务：如果一个Service被某个Android组件调用bindService()方法绑定服务，同样因为一个Service只会被创建一个实例，
 * 所以不管bindService()被不同的组件调用几次，onCreate()方法都只被回调一次，转而还会执行onBind()方法进行Binder对象的绑定。
 * 在绑定连接被建立后，Service将一直运行下去，除非宿主调用unbindService()方法断开连接或者之前的宿主被销毁了，
 * 这个时候系统会检测这个Service是否存在宿主绑定，当宿主绑定的数量为0的时候，系统将会自动停止服务，对应的onDestroy()将被回调。
 * </p>
 * <p>
 * Android系统仅会为一个Service创建一个实例，所以不管是使用启动服务或是绑定服务，都操作的是同一个Service实例。
 * 但是如果两种服务运行方式均被调用，那么绑定服务将会转为启动服务运行，这时就算之前绑定的宿主被销毁了，
 * 也不会影响服务的运行，而启动服务并不会因为有宿主调用了bindService()方法而把原本的启动服务转为绑定服务，
 * 但是还是会与宿主产生绑定，但这时即使宿主解除绑定后，服务依然按启动服务的生命周期在后台运行，
 * 直到有Context调用了stopService()或是服务本身调用了stopSelf()方法才会真正销毁服务。
 * 因此对于一个既使用startService()启动又使用bindService()绑定的服务，除非这个服务的两条生命周期均完结，否则不会被销毁。
 * 也就是说，在不考虑系统在资源不足的时候，主动回收资源销毁服务的情况下，使用startService()启动的服务，
 * 必须使用stopService()或是服务本身的stopSelf()停止服务，使用bindService()绑定的服务，
 * 必须使用unbindService()或是销毁宿主来解除绑定，否则服务一直运行
 * </p>
 * <p>
 * 一些情况可以考虑下用这三个：{@link JobService}、{@link IntentService}、{@link JobIntentService}
 * </p>
 * <p>
 * 启动前台服务用这个方式，并且需要在{@link BaseService#onCreate}
 * 或者{@link BaseService#onStartCommand}中使用{@link Service#startForeground}发送一条前台Notification:
 * <code>
 * if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
 * return context.startForegroundService(intent);
 * } else {
 * return context.startService(intent);
 * }
 * </code>
 * </p>
 * 如果需要结束前台则可以用：
 * <code>
 * stopForeground(true);
 * </code>
 */
public abstract class BaseService extends Service {

    public static final int BIND_FLAG_AUTO_CREATE_AND_ALWAYS_START = Context.BIND_WAIVE_PRIORITY | Context.BIND_AUTO_CREATE;
    public static final int BIND_FLAG_AUTO_CREATE_AND_ALWAYS_START_IN_BG = Context.BIND_WAIVE_PRIORITY | Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND;

    public static ComponentName toStartService(Intent intent) {
        return Utils.requireNonNull(BaseApplication.getAppContext()).startService(intent);
    }

    public static ComponentName toStartForegroundService(@NonNull Intent intent) {
        //5.0之后需添加包名
        if (TextUtils.isEmpty(intent.getPackage())) {
            intent.setPackage(BaseApplication.getAppContext().getPackageName());
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            return Utils.requireNonNull(BaseApplication.getAppContext()).startForegroundService(intent);
        } else {
            return toStartService(intent);
        }
    }

    public static boolean bindNormalService(Context context, Intent intent, ServiceConnection serviceConnection) {
        return Utils.requireNonNull(context).bindService(intent, serviceConnection, BIND_FLAG_AUTO_CREATE_AND_ALWAYS_START);
    }

    public static boolean bindBackgroundService(Context context, Intent intent, ServiceConnection serviceConnection) {
        return Utils.requireNonNull(context).bindService(intent, serviceConnection, BIND_FLAG_AUTO_CREATE_AND_ALWAYS_START_IN_BG);
    }

    public static boolean bindNormalService(Intent intent, ServiceConnection serviceConnection) {
        return bindNormalService(BaseApplication.getAppContext(), intent, serviceConnection);
    }

    public static boolean bindBackgroundService(Intent intent, ServiceConnection serviceConnection) {
        return bindBackgroundService(BaseApplication.getAppContext(), intent, serviceConnection);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    /**
     * 服务被系统销毁后，会根据该方法返回值决定如何继续服务
     * <p>
     * 1、返回{@link Service#START_NOT_STICKY}，在系统销毁服务后，不重新创建服务，除非有额外的Intent启动服务
     * 2、返回{@link Service#START_STICKY}，则在系统销毁服务后，重新创建服务和调用onStartCommand()，
     * 但会依照一个空的Intent对象执行任务，就如仅开始服务，但不执行命令，等待服务的继续工作
     * 3、返回{@link Service#START_REDELIVER_INTENT}，会在系统销毁服务后，重新创建和调用onStartCommand()方法，
     * 依据之前启动它的Intent对象开启服务，进行之前未执行完的任务，如下载文件
     * </p>
     *
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 多个client绑定服务，返回的IBinder有可能不是一样的，这个取决于自己的业务需要
     *
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
